<template>
	<el-container>
		<el-aside style="width: 200px;">
			<el-container>
				<el-header>
					<el-button style="padding: 0px" type="text" icon="el-icon-delete" size="large" @click="deleteElement" :disabled="!this.activeElement.type"></el-button>
					<el-divider direction="vertical"></el-divider>
					<el-button style="padding: 0px" type="text" icon="el-icon-download" size="large" @click="downloadData"></el-button>
					<el-divider direction="vertical"></el-divider>
					<el-button style="padding: 0px" type="text" icon="el-icon-zoom-in" size="large" @click="zoomAdd"></el-button>
					<el-divider direction="vertical"></el-divider>
					<el-button style="padding: 0px" type="text" icon="el-icon-zoom-out" size="large" @click="zoomSub"></el-button>
				</el-header>
				<el-main class="nopadding">
					<node-menu @addNode="addNode" ref="nodeMenu"></node-menu>
				</el-main>
			</el-container>
		</el-aside>
		<el-container>
			<el-header>
				<el-link style="width: 100%" type="primary" :underline="false">{{ data.name }}</el-link>
			</el-header>
			<el-main class="nopadding">
				<div v-if="easyFlowVisible" style="height:100%;">
					<div id="efContainer" ref="efContainer" style="width: 100%;height: 100%;" class="container" v-flowDrag :style="{'background-image': `url(img/designer-bg.png)`}">
						<flow-node
							v-for="node in data.nodeList" :key="node.id"
							:id="node.id"
							:node="node"
							:formFields="this.formFieldList"
							:activeElement="activeElement"
							@changeNodeSite="changeNodeSite"
							@nodeRightMenu="nodeRightMenu"
							@clickNode="clickNode"
						>
						</flow-node>
					</div>
				</div>
			</el-main>
		</el-container>
		<el-aside>
			<el-container>
				<el-header>
					<el-button type="info" plain round icon="el-icon-document" @click="dataInfo" size="mini"></el-button>
					<el-button type="warning" @click="testResult" size="mini">测试</el-button>
					<el-button type="primary" icon="el-icon-info-filled" @click="openHelp" size="mini">帮助</el-button>
				</el-header>
				<el-main class="nopadding">
					<div v-show="isShow&&isChild==false">
						<flow-node-form ref="nodeForm" @setLineLabel="setLineLabel" @repaintEverything="repaintEverything" :flow-id="this.flowId"></flow-node-form>
					</div>
					<div style="width: 300px;border-left: 1px solid #dce3e8;background-color: #FBFBFB" v-show="isShow&&isChild">
						<flow-child-node-form ref="childNodeForm" @setLineLabel="setLineLabel" @repaintEverything="repaintEverything" :flow-id="this.flowId"></flow-child-node-form>
					</div>
				</el-main>
			</el-container>
		</el-aside>
	</el-container>
	<flow-info v-if="flowInfoVisible" ref="flowInfo" :data="data"></flow-info>
	<flow-help v-if="flowHelpVisible" ref="flowHelp"></flow-help>
	<testResult v-if="testResultVisible" ref="testResult" :flow-id="this.flowId"></testResult>
</template>

<script>
import {cyFlowMixin} from './mixins'
import flowNode from './node'
import nodeMenu from './node_menu'
import FlowInfo from './info'
import FlowHelp from './help'
import FlowNodeForm from './node_form'
import FlowChildNodeForm from './child_node_form'
import lodash from 'lodash'
import jsPlumbTools from 'jsplumb'
import testResult from "./test_result";

let jsPlumb = jsPlumbTools.jsPlumb;
export default {
	data() {
		return {
			isShow: false,
			isChild: false,
			formFieldList: [],
			// jsPlumb 实例
			//jsPlumb: jsPlumbTools.jsPlumb,
			// 控制画布销毁
			easyFlowVisible: true,
			// 控制流程数据显示与隐藏
			flowInfoVisible: false,
			testResultVisible:false,
			// 是否加载完毕标志位
			loadEasyFlowFinish: false,
			flowHelpVisible: false,
			// 数据
			data: {},
			// 激活的元素、可能是节点、可能是连线
			activeElement: {
				// 可选值 node 、line
				type: undefined,
				// 节点ID
				nodeId: undefined,
				// 连线ID
				name: "",
				sourceId: undefined,
				targetId: undefined
			},
			zoom: 1,
			flowId: this.$route.query.flowId
		}
	},
	// 一些基础配置移动该文件中
	mixins: [cyFlowMixin],
	components: {
		testResult,
		flowNode, nodeMenu, FlowInfo, FlowNodeForm, FlowHelp, FlowChildNodeForm
	},
	directives: {
		'flowDrag': {
			bind(el, binding, vnode, oldNode) {
				if (!binding) {
					return
				}
				el.onmousedown = (e) => {
					if (e.button == 2) {
						// 右键不管
						return
					}
					//  鼠标按下，计算当前原始距离可视区的高度
					let disX = e.clientX
					let disY = e.clientY
					el.style.cursor = 'move'

					document.onmousemove = function (e) {
						// 移动时禁止默认事件
						e.preventDefault()
						const left = e.clientX - disX
						disX = e.clientX
						el.scrollLeft += -left

						const top = e.clientY - disY
						disY = e.clientY
						el.scrollTop += -top
					}

					document.onmouseup = function (e) {
						el.style.cursor = 'auto'
						document.onmousemove = null
						document.onmouseup = null
					}
				}
			}
		}
	},
	mounted() {
		this.$nextTick(() => {
			this.dataReload()
		})
		this.getBpmFieldList();
	},
	methods: {
		testResult(){
			this.testResultVisible = true
			this.$nextTick(function () {
				this.$refs.testResult.open();
			})
		},
		// 返回唯一标识
		getUUID() {
			return Math.random().toString(36).substr(3, 10)
		},
		jsPlumbInit() {
			jsPlumb.ready(() => {
				// 导入默认配置
				jsPlumb.importDefaults(this.jsplumbSetting)
				// 会使整个jsPlumb立即重绘。
				jsPlumb.setSuspendDrawing(false, true);
				// 初始化节点
				this.loadEasyFlow()
				jsPlumb.bind('click', (conn, originalEvent) => {
					var conditionItem = [];
					var condition = '';
					this.data.lineList.forEach(function (line) {
						if (line.from == conn.sourceId && line.to == conn.targetId) {
							if (line.condition) {
								condition = line.condition
							}
							if (line.conditionItem) {
								conditionItem = line.conditionItem
							}
						}
					})
					this.isChild = false;
					this.isShow = true;
					this.activeElement.type = 'line'
					this.activeElement.sourceId = conn.sourceId
					this.activeElement.targetId = conn.targetId
					this.$refs.nodeForm.lineInit({
						from: conn.sourceId,
						to: conn.targetId,
						condition: condition,
						conditionItem: conditionItem,
						label: conn.getLabel()
					},this.data)
				})
				// 连线
				jsPlumb.bind("connection", (evt) => {
					let from = evt.source.id
					let to = evt.target.id
					if (this.loadEasyFlowFinish) {
						this.data.lineList.push({from: from, to: to});
						this.data.nodeList.forEach(item=>{
							if(item.id==from)
							{
								let stepNextArr = [];
								if(item.nextStep!=undefined&&item.nextStep!=null&&item.nextStep!='')
								{
									stepNextArr = item.nextStep.split(",");
								}
								stepNextArr.push(to)
								item.nextStep = stepNextArr.join(",");
							}
						})
					}
				})

				// 删除连线回调
				jsPlumb.bind("connectionDetached", (evt) => {
					this.deleteLine(evt.sourceId, evt.targetId)
				})

				// 改变线的连接节点
				jsPlumb.bind("connectionMoved", (evt) => {
					this.changeLine(evt.originalSourceId, evt.originalTargetId)
				})

				// 连线右击
				jsPlumb.bind("contextmenu", (evt) => {
					console.log('contextmenu', evt)
				})

				// 连线
				jsPlumb.bind("beforeDrop", (evt) => {
					let from = evt.sourceId
					let to = evt.targetId
					let nodeType = "";
					this.data.nodeList.forEach(item => {
						if (item.id == from) {
							nodeType = item.type;
						}
					})
					if (nodeType == "childFlow") {
						let flag0 = false;
						this.data.lineList.forEach(item => {
							if (item.from == from) {
								flag0 = true;
							}

						})
						if (flag0) {
							this.$message.error('子公文不能指向多个节点')
							return false
						}
					}
					if (from === to) {
						this.$message.error('节点不支持连接自己')
						return false
					}
					if (this.hasLine(from, to)) {
						this.$message.error('该关系已存在,不允许重复创建')
						return false
					}
					//判断是否可回环
					/*if (this.hashOppositeLine(from, to)) {
						this.$message.error('不支持两个节点之间连线回环');
						return false
					}*/
					this.connectionBpmStepLine(from, to);
					return true;
				})

				// beforeDetach
				jsPlumb.bind("beforeDetach", (evt) => {
					console.log('beforeDetach', evt)
				})
				jsPlumb.setContainer(this.$refs.efContainer)
			})
		},
		// 加载流程图
		loadEasyFlow() {
			let than_ = this
			// 初始化节点
			for (var i = 0; i < this.data.nodeList.length; i++) {
				let node = this.data.nodeList[i]
				// 设置源点，可以拖出线连接其他节点
				jsPlumb.makeSource(node.id, lodash.merge(this.jsplumbSourceOptions, {}))
				// // 设置目标点，其他源点拖出的线可以连接该节点
				jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions)
				if (!node.viewOnly) {
					jsPlumb.draggable(node.id, {
						containment: 'parent',
						stop: async function (el) {
							var params = {flowId: than_.flowId, stepId: node.id, stepLeft: node.left, stepTop: node.top};
							var res = await than_.$API.bpm.bpmStep.update.post(params);
							if (res.code == 200) {
								than_.$message.success("操作成功")
							} else {
								than_.$alert(res.message, "提示", {type: 'error'})
							}
						}
					})
				}
			}
			// 初始化连线
			if(this.data.lineList!=undefined)
			{
				for (var s = 0; s < this.data.lineList.length; s++) {
					let line = this.data.lineList[s]
					var connParam = {
						source: line.from,
						target: line.to,
						label: line.label ? line.label : '',
						condition: line.condition ? line.condition : '',
						conditionItem: line.conditionItem ? line.conditionItem : [],
						connector: line.connector ? line.connector : '',
						anchors: line.anchors ? line.anchors : undefined,
						paintStyle: line.paintStyle ? line.paintStyle : undefined,
					}
					jsPlumb.connect(connParam, this.jsplumbConnectOptions)
				}
			}
			this.$nextTick(function () {
				this.loadEasyFlowFinish = true
			})
		},
		// 设置连线条件
		setLineLabel(from, to, label, condition, conditionItem) {
			var conn = jsPlumb.getConnections({
				source: from,
				target: to
			})[0]
			if (!label || label === '') {
				conn.removeClass('flowLabel')
				conn.addClass('emptyFlowLabel')
			} else {
				conn.addClass('flowLabel')
			}
			conn.setLabel({
				label: label,
			})
			this.data.lineList.forEach(function (line) {
				if (line.from == from && line.to == to) {
					line.label = label
					line.condition = condition
					line.conditionItem = conditionItem
				}
			})
		},
		// 删除激活的元素
		deleteElement() {
			if (this.activeElement.type === 'node') {
				this.deleteNode(this.activeElement.nodeId, this.activeElement.name)
			} else if (this.activeElement.type === 'line') {
				this.$confirm('确定删除所点击的线吗?', '提示', {
					confirmButtonText: '确定',
					cancelButtonText: '取消',
					type: 'warning'
				}).then(async () => {
					var conn = jsPlumb.getConnections({
						source: this.activeElement.sourceId,
						target: this.activeElement.targetId
					})[0]
					var res = await this.$API.bpm.bpmStep.deleteBpmStepLine.post({flowId: this.flowId, sourceId: this.activeElement.sourceId, targetId: this.activeElement.targetId});
					if (res.code == 200) {
						this.$message.success("操作成功");
						jsPlumb.deleteConnection(conn);
						this.data.nodeList.forEach(item=>{
							if(item.id==this.activeElement.sourceId)
							{
								let stepNextArr = [];
								if(item.nextStep!=undefined&&item.nextStep!=null&item.nextStep!='')
								{
									stepNextArr = item.nextStep.split(",");
									let index = stepNextArr.indexOf(this.activeElement.targetId);
									if(index>=0)
									{
										stepNextArr.splice(index, 1)
									}
								}
								if(stepNextArr.length>0)
								{
									item.nextStep = stepNextArr.join(",");
								}else{
									item.nextStep = "";
								}
							}
						})
					} else {
						this.$alert(res.message, "提示", {type: 'error'})
					}

				}).catch(() => {
				})
			}
		},
		// 删除线
		deleteLine(from, to) {
			this.data.lineList = this.data.lineList.filter(function (line) {
				if (line.from == from && line.to == to) {
					return false
				}
				return true
			})
		},
		// 改变连线
		changeLine(oldFrom, oldTo) {
			this.deleteLine(oldFrom, oldTo)
		},
		// 改变节点的位置
		changeNodeSite(data) {
			for (var i = 0; i < this.data.nodeList.length; i++) {
				let node = this.data.nodeList[i]
				if (node.id === data.nodeId) {
					node.left = data.left
					node.top = data.top
				}
			}
		},
		/**
		 * 拖拽结束后添加新的节点
		 * @param evt
		 * @param nodeMenu 被添加的节点对象
		 * @param mousePosition 鼠标拖拽结束的坐标
		 */
		addNode(evt, nodeMenu, mousePosition) {
			var haveStartNode = false;
			var endStartNode = false;
			this.data.nodeList.forEach(item => {
				if (item.type == "start" && nodeMenu.type == "start") {
					haveStartNode = true;
				}
				if (item.type == "end" && nodeMenu.type == "end") {
					endStartNode = true;
				}
			})
			if (haveStartNode) {
				this.$message.error("公文中只能有一个开始节点");
				return;
			}
			if (endStartNode) {
				this.$message.error("公文中只能有一个结束节点");
				return;
			}
			var screenX = evt.originalEvent.clientX, screenY = evt.originalEvent.clientY
			let efContainer = this.$refs.efContainer
			var containerRect = efContainer.getBoundingClientRect()
			var left = screenX, top = screenY
			// 计算是否拖入到容器中
			if (left < containerRect.x || left > containerRect.width + containerRect.x || top < containerRect.y || containerRect.y > containerRect.y + containerRect.height) {
				this.$message.error("请把节点拖入到画布中")
				return
			}
			left = left - containerRect.x + efContainer.scrollLeft
			top = top - containerRect.y + efContainer.scrollTop
			// 居中
			left -= 85
			top -= 16
			var nodeId = this.getUUID()
			// 动态生成名字
			var origName = nodeMenu.name
			var nodeName = origName
			var index = 1
			while (index < 10000) {
				var repeat = false
				for (var i = 0; i < this.data.nodeList.length; i++) {
					let node = this.data.nodeList[i]
					if (node.name === nodeName) {
						nodeName = origName + index
						repeat = true
					}
				}
				if (repeat) {
					index++
					continue
				}
				break
			}
			var node = {
				id: nodeId,
				name: nodeName,
				type: nodeMenu.type,
				flowId: this.flowId,
				left: left + 'px',
				top: top + 'px',
				ico: nodeMenu.ico,
				sortNo: 1,
				state: '1',
				remark: '',
				changeTitle: '0',
				follow: '0',
				addStep: '0',
				approvalFlag: '1',
				approval: '1',
				passEndFlag: '0',
				parentWait: '0',
				waitStepId: '',
				goBack: '0',
				sameJump: '0',
				concurrentFlag: '0',
				gatherFlag: '0',
				sendToRole:'0',
				passTime: 0,
				userLevel:'',
				userRole: '',
				deptRole: '',
				levelRole: '',
				writerField: [],
				xUploadRole: '0',
				publicFileRole: '1',
				bpmLinkFlag:'1',
				childTableRowRole: '',
				childTableRole: '',
				hideField: [],
				changeUser: '1',
				opRule: '0',
				doRule: '0',
				autoUserModel: '1',
				defaultAuto: '',
				defaultAutoOther: '',
				beforeClass: '',
				beforeParam: '',
				afterClass: '',
				afterParam: '',
				autoSendType: '0',
				remindNextUser: '0',
				remindCreateUser: '0',
				remindParticipant: '0',
			}
			/**
			 * 这里可以进行业务判断、是否能够添加该节点
			 */
			this.createBpmStep(node);

		},
		/**
		 * 删除节点
		 * @param nodeId 被删除节点的ID
		 */
		deleteNode(nodeId, name) {
			this.$confirm('确定要删除【' + name + "】节点:" + nodeId + '?', '提示', {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning',
				closeOnClickModal: false
			}).then(async () => {
				/**
				 * 这里需要进行业务判断，是否可以删除
				 */
				var res = await this.$API.bpm.bpmStep.delete.post({stepId: nodeId});
				if (res.code == 200) {
					this.data.nodeList = this.data.nodeList.filter(function (node) {
						if (node.id === nodeId) {
							return false
						}
						return true
					})
					this.$nextTick(function () {
						jsPlumb.removeAllEndpoints(nodeId);
					})
					this.$message.success("操作成功")
				} else {
					this.$alert(res.message, "提示", {type: 'error'})
				}
			}).catch(() => {
			})
			return true
		},
		clickNode(nodeId, name, type, formFields) {
			if (type == "childFlow") {
				this.isShow = true;
				this.isChild = true;
				this.activeElement.type = 'node'
				this.activeElement.nodeId = nodeId
				this.activeElement.name = name
				this.$refs.childNodeForm.childNodeInit(this.data, nodeId, formFields)
			} else {
				if (type != "end") {
					this.isChild = false;
					this.isShow = true;
					this.activeElement.type = 'node'
					this.activeElement.nodeId = nodeId
					this.activeElement.name = name
					this.$refs.nodeForm.nodeInit(this.data, nodeId, formFields)
				} else {
					this.$alert("结束节点不可设置！", "提示", {type: 'error'})
				}
			}

		},
		// 是否具有该线
		hasLine(from, to) {
			for (var i = 0; i < this.data.lineList.length; i++) {
				var line = this.data.lineList[i]
				if (line.from === from && line.to === to) {
					return true
				}
			}
			return false
		},
		// 是否含有相反的线
		hashOppositeLine(from, to) {
			return this.hasLine(to, from)
		},
		nodeRightMenu(nodeId, evt) {
			this.menu.show = true
			this.menu.curNodeId = nodeId
			this.menu.left = evt.x + 'px'
			this.menu.top = evt.y + 'px'
		},
		repaintEverything() {
			jsPlumb.repaint()
		},
		// 流程数据信息
		dataInfo() {
			this.flowInfoVisible = true
			this.$nextTick(function () {
				this.$refs.flowInfo.init()
			})
		},
		// 加载流程图
		dataReload() {
			this.easyFlowVisible = false
			this.$nextTick(async () => {
				var res = await this.$API.bpm.bpmStep.getBpmStepConfigByFlowId.get({flowId: this.flowId});
				if (res.code == 200) {
					this.easyFlowVisible = true
					this.data = res.data;
					jsPlumb = jsPlumbTools.jsPlumb.getInstance();
					this.$nextTick(() => {
						this.jsPlumbInit()
					})
				} else {
					this.$alert(res.message, "提示", {type: 'error'})
				}
			})
		},
		zoomAdd() {
			if (this.zoom >= 1) {
				return
			}
			this.zoom = this.zoom + 0.1
			this.$refs.efContainer.style.transform = `scale(${this.zoom})`
			jsPlumb.setZoom(this.zoom)
		},
		zoomSub() {
			if (this.zoom <= 0) {
				return
			}
			this.zoom = this.zoom - 0.1
			this.$refs.efContainer.style.transform = `scale(${this.zoom})`
			jsPlumb.setZoom(this.zoom)
		},
		// 下载数据
		downloadData() {
			this.$confirm('确定要下载该公文数据吗？', '提示', {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning',
				closeOnClickModal: false
			}).then(() => {
				var datastr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(this.data, null, '\t'));
				var downloadAnchorNode = document.createElement('a')
				downloadAnchorNode.setAttribute("href", datastr);
				downloadAnchorNode.setAttribute("download", 'data.json')
				downloadAnchorNode.click();
				downloadAnchorNode.remove();
				this.$message.success("正在下载中,请稍后...")
			}).catch(() => {
			})
		},
		openHelp() {
			this.flowHelpVisible = true
			this.$nextTick(function () {
				this.$refs.flowHelp.init()
			})
		},
		async connectionBpmStepLine(sourceId, targetId) {
			var res = await this.$API.bpm.bpmStep.connectionBpmStepLine.post({flowId: this.flowId, sourceId: sourceId, targetId: targetId});
			if (res.code == 200) {
				this.$message.success('连接成功')
			} else {
				this.$alert(res.message, "提示", {type: 'error'})
			}
		},
		async createBpmStep(node) {
			var res = await this.$API.bpm.bpmStep.insert.post(node);
			if (res.code == 200) {
				node.id = res.data.stepId;
				this.data.nodeList.push(node)
				this.$nextTick(function () {
					jsPlumb.makeSource(node.id, this.jsplumbSourceOptions)
					jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions)
					jsPlumb.draggable(node.id, {
						containment: 'parent',
						stop: function (el) {
							// 拖拽节点结束后的对调
							console.log('拖拽结束: ', el)
						}
					})
				})
				this.$message.success("操作成功")
			} else {
				this.$alert(res.message, "提示", {type: 'error'})
			}
		},
		async getBpmFieldList() {
			var res = await this.$API.bpm.bpmForm.getBpmFormFieldsListForSelect.get({flowId: this.flowId});
			if (res.code == 200) {
				if (res.data == undefined) {
					this.$alert("未能检测到表单字段", "提示", {type: 'error'})
				} else {
					this.formFieldList = res.data;
				}
			}
		},
	}
}
</script>
<style scoped>
@import './index.css';
</style>
